package com.appistry.management.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.appistry.management.Application;
import com.appistry.management.ApplicationInfo;
import com.appistry.management.ConsoleExtension;
import com.appistry.management.FabricArchive;
import com.appistry.management.FabricArchiveInfo;
import com.appistry.management.FabricFile;
import com.appistry.management.FabricInfo;
import com.appistry.management.FabricOverview;
import com.appistry.management.Manifest;
import com.appistry.management.ManifestEntry;
import com.appistry.management.Service;
import com.appistry.management.ServiceApp;
import com.appistry.management.ServiceAppInfo;
import com.appistry.management.ServiceConfig;
import com.appistry.management.ServiceConfigInfo;
import com.appistry.management.ServiceInfo;
import com.appistry.management.ShutdownList;
import com.appistry.management.ShutdownListInfo;
import com.appistry.management.State;
import com.appistry.management.Worker;
import com.appistry.management.WorkerInfo;
import com.appistry.management.WorkerPerformanceInfo;
import com.appistry.management.domain.impl.ApplicationImpl;
import com.appistry.management.domain.impl.ApplicationInfoImpl;
import com.appistry.management.domain.impl.ConfigImpl;
import com.appistry.management.domain.impl.ConsoleExtensionsImpl;
import com.appistry.management.domain.impl.FabricArchiveImpl;
import com.appistry.management.domain.impl.FabricArchiveInfoImpl;
import com.appistry.management.domain.impl.FabricFileImpl;
import com.appistry.management.domain.impl.FabricInfoImpl;
import com.appistry.management.domain.impl.FabricOverviewImpl;
import com.appistry.management.domain.impl.ManifestEntryImpl;
import com.appistry.management.domain.impl.ManifestFileImpl;
import com.appistry.management.domain.impl.ManifestImpl;
import com.appistry.management.domain.impl.ServiceAppImpl;
import com.appistry.management.domain.impl.ServiceAppInfoImpl;
import com.appistry.management.domain.impl.ServiceConfigImpl;
import com.appistry.management.domain.impl.ServiceConfigInfoImpl;
import com.appistry.management.domain.impl.ServiceImpl;
import com.appistry.management.domain.impl.ServiceInfoImpl;
import com.appistry.management.domain.impl.ShutdownListImpl;
import com.appistry.management.domain.impl.ShutdownListInfoImpl;
import com.appistry.management.domain.impl.WorkerImpl;
import com.appistry.management.domain.impl.WorkerInfoImpl;
import com.appistry.management.domain.impl.WorkerPerformanceInfoImpl;
import com.appistry.management.rest.RestFabricManager;

public class JsonTransformUtil {
	private static final String ACTIVE = "active";
	private static final String ACTIVE_VERSION = "active_version";
	private static final String STAGED_VERSION = "staged_version";
	private static final String FILENAME = "filename";
	private static final String DESCRIPTION = "description";
	private static final String STAGED_CHANGES = "staged_changes";
	private static final String VERSION = "version";
	private static final String DISPLAY_NAME = "display_name";
	private static final String FILE = "file";
	private static final String HREF = "href";
	private static final String CONFIG = "config";
	private static final String APP = "app";
	private static final String NAME = "name";
	private static final String URL = "url";
	private static final String FAR = "far";
	private static final String ADMIN_PORT = "admin_port";
	private static final String FABRIC_ADDRESS = "fabric_address";
	private static final String KEEPER_LEADER = "keeper_leader";
	private static final String REQUEST_PORT = "request_port";
	private static final String VERSION_HASH = "version_hash";
	private static final String SPACE = " ";
	private static final String UNDERSCORE = "_";
	private static final String CPU_PERCENT = "cpu_percent";
	private static final String FAM_SPACE_CAPACITY = "fam_space_capacity";
	private static final String MEMORY_AVAILABLE = "memory_available";
	private static final String MEMORY_TOTAL = "memory_total";
	private static final String STATE = "state";
	private static final String TPS = "tps";
	private static final String SERVICE = "service";
	private static final String IP = "ip";
	private static final String MAC = "mac";
	private static final String OPERATING_SYSTEM = "operating_system";
	private static final String CPU_COUNT = "cpu_count";
	private static final String REGION_PORT = "region_port";
	private static final String REQUEST_SERVICE_UP = "request_service_up";
	private static final String REGION_LEADER = "region_leader";
	private static final String RQUEST_SSL_SERVICE_UP = "request_ssl_service_up";
	private static final String INTERNAL_IP = "internal_ip";
	private static final String EXTERNAL_IP = "external_ip";
	private static final String CLOCK = "clock";
	private static final String ELAPSED = "elapsed";
	private static final String RESTART = "restart";
	private static final String REBOOT= "reboot";
	private static final String RESTART_SERVICE = "restart-service";
	private static final String RESTART_STORAGE = "restart-storage";
	private static final String RESTART_FAM = "restart-fam";

	public static ApplicationInfo toWorkerFileMetaDataMap(String json) throws JSONException {
		JSONObject jsonObj = new JSONObject(json);

		ApplicationInfoImpl applicationInformation = new ApplicationInfoImpl();
		applicationInformation.setDescription(jsonObj.getString(DESCRIPTION));
		applicationInformation.setVersion(jsonObj.getString(VERSION));
		applicationInformation.setDisplayName(jsonObj.getString(DISPLAY_NAME));
		applicationInformation.setFile(jsonObj.getString(FILE));
		applicationInformation.setHref(jsonObj.getString(HREF));

		Object tmpObject = jsonObj.get(CONFIG);

		if (tmpObject != null && !tmpObject.equals(JSONObject.NULL)) {
			JSONObject jsonConfigObj = (JSONObject) tmpObject;
			ConfigImpl config = new ConfigImpl();
			config.setFile(jsonConfigObj.getString(FILE));
			config.setHref(jsonConfigObj.getString(HREF));
			config.setVersion(jsonConfigObj.getString(VERSION));
			applicationInformation.setConfig(config);
		}

		return applicationInformation;
	}

	public static FabricArchiveInfo toFabricArchiveInfo(String json) throws JSONException {
		JSONObject jsonObj = new JSONObject(json);

		FabricArchiveInfoImpl farInfo = new FabricArchiveInfoImpl();
		farInfo.setDescription(jsonObj.getString(DESCRIPTION));
		farInfo.setVersion(jsonObj.getString(VERSION));
		farInfo.setDisplayName(jsonObj.getString(DISPLAY_NAME));
		farInfo.setFile(jsonObj.getString(FILE));

		return farInfo;
	}

	public static List<Application> toApplicationList(RestFabricManager restFabricManager, String json) throws JSONException {
		ArrayList<Application> apps = new ArrayList<Application>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			ApplicationImpl app = new ApplicationImpl(restFabricManager, jsonObj.getString(APP));
			apps.add(app);
		}

		return apps;
	}

	@SuppressWarnings("unchecked")
	public static List<ConsoleExtension> toConsoleExtensionList(String json) throws JSONException {
		ArrayList<ConsoleExtension> consoleExtensions = new ArrayList<ConsoleExtension>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			ConsoleExtensionsImpl consoleExtension = new ConsoleExtensionsImpl();

			// Add required vars
			consoleExtension.setName(jsonObj.getString(NAME));
			consoleExtension.setUrl(jsonObj.getString(URL));

			// Add all vars to map
			HashMap<String, String> allKeyValPairs = new HashMap<String, String>();
			Iterator<String> itr = jsonObj.keys();
			while (itr.hasNext()) {
				String key = itr.next();
				allKeyValPairs.put(key, jsonObj.getString(key));
			}
			consoleExtension.setKeyValPairs(allKeyValPairs);

			// Add consoleExtension to list
			consoleExtensions.add(consoleExtension);
		}

		return consoleExtensions;
	}

	public static List<FabricArchive> toFabricArchiveList(RestFabricManager restFabricManager, String json) throws JSONException {
		ArrayList<FabricArchive> fars = new ArrayList<FabricArchive>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			FabricArchiveImpl far = new FabricArchiveImpl(restFabricManager, jsonObj.getString(FAR));
			fars.add(far);
		}

		return fars;
	}

	public static FabricInfo toFabricInfo(String json) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);

		FabricInfoImpl fabricInfo = new FabricInfoImpl();
		fabricInfo.setDescription(jsonObject.getString(DESCRIPTION));
		fabricInfo.setName(jsonObject.getString(NAME));

		return fabricInfo;
	}

	public static FabricOverview toFabricOverview(String json) throws JSONException {
		try {
			JSONObject jsonObject = new JSONObject(json);

			FabricOverviewImpl fabricOverview = new FabricOverviewImpl();

			fabricOverview.setAdminPort(jsonObject.getInt(ADMIN_PORT));
			fabricOverview.setDescription(jsonObject.getString(DESCRIPTION));
			fabricOverview.setFabricAddress(InetAddress.getByName(jsonObject.getString(FABRIC_ADDRESS)));
			fabricOverview.setKeeperLeader(InetAddress.getByName(jsonObject.getString(KEEPER_LEADER)));
			fabricOverview.setName(jsonObject.getString(NAME));
			fabricOverview.setRequestPort(jsonObject.getInt(REQUEST_PORT));
			fabricOverview.setVersion(jsonObject.getString(VERSION));
			fabricOverview.setVersionHash(jsonObject.getString(VERSION_HASH));

			return fabricOverview;

		} catch (UnknownHostException uhe) {
			// this should never happen
			throw new RuntimeException(uhe);
		}
	}

	public static List<FabricFile> toFabricFilesList(RestFabricManager restFabricManager, String json) throws JSONException {
		ArrayList<FabricFile> files = new ArrayList<FabricFile>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			FabricFileImpl file = new FabricFileImpl(restFabricManager, jsonObj.getString(FILE));
			files.add(file);
		}

		return files;
	}

	public static Manifest toManifest(String json) throws JSONException {
		ManifestImpl workerManifest = new ManifestImpl();
		JSONObject jsonObject = new JSONObject(json);

		// Active Manifest
		JSONObject activeManifest = jsonObject.getJSONObject(ACTIVE);
		Iterator<?> activeManifestItr = activeManifest.keys();
		while (activeManifestItr.hasNext()) {
			String entry = (String) activeManifestItr.next();

			ManifestFileImpl manifestFile = new ManifestFileImpl();
			manifestFile.setEntry(entry);
			manifestFile.setFilename(activeManifest.getJSONObject(entry).getString(FILENAME));
			manifestFile.setVersion(activeManifest.getJSONObject(entry).getString(VERSION));

			workerManifest.addActiveManifestFile(manifestFile);
		}

		// Staged Manifest
		if (jsonObject.has(STAGED_CHANGES)) {
			JSONObject stagedManifest = jsonObject.getJSONObject(STAGED_CHANGES);
			Iterator<?> stagedManifestItr = stagedManifest.keys();
			while (stagedManifestItr.hasNext()) {
				String entry = (String) stagedManifestItr.next();

				ManifestFileImpl manifestFile = new ManifestFileImpl();
				manifestFile.setEntry(entry);
				manifestFile.setFilename(activeManifest.getJSONObject(entry).getString(FILENAME));
				manifestFile.setVersion(activeManifest.getJSONObject(entry).getString(VERSION));

				workerManifest.addStagedManifestFile(manifestFile);
			}
		}

		return workerManifest;
	}

	public static ManifestEntry toManifestEntry(String json) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);
		ManifestEntryImpl workerManifestEntry = new ManifestEntryImpl();

		workerManifestEntry.setActiveVersion(jsonObject.getString(ACTIVE_VERSION));
		workerManifestEntry.setStagedVersion(jsonObject.getString(STAGED_VERSION));
		workerManifestEntry.setFilename(jsonObject.getString(FILENAME));

		return workerManifestEntry;
	}

	public static Map<InetAddress, WorkerPerformanceInfo> toFabricPerformanceInfo(String json) throws JSONException {
		try {
			JSONObject jsonObject = new JSONObject(json);

			Iterator<?> itr = jsonObject.keys();

			HashMap<InetAddress, WorkerPerformanceInfo> map = new HashMap<InetAddress, WorkerPerformanceInfo>();

			while (itr.hasNext()) {
				String ip = (String) itr.next();
				JSONObject workerJson = jsonObject.getJSONObject(ip);
				WorkerPerformanceInfoImpl worker = toWorkerPerformanceInfo(workerJson.toString());
				map.put(InetAddress.getByName(ip), worker);
			}

			return map;
		} catch (UnknownHostException uhe) {
			// this should never happen
			throw new RuntimeException(uhe);
		}
	}

	public static WorkerPerformanceInfoImpl toWorkerPerformanceInfo(String json) throws JSONException {
		JSONObject jsonObj = new JSONObject(json);

		WorkerPerformanceInfoImpl worker = new WorkerPerformanceInfoImpl();
		worker.setCpuPercent(jsonObj.getInt(CPU_PERCENT));
		worker.setFamSpaceCapacity(jsonObj.getLong(FAM_SPACE_CAPACITY));
		worker.setMemoryAvailable(jsonObj.getLong(MEMORY_AVAILABLE));
		worker.setMemoryTotal(jsonObj.getLong(MEMORY_TOTAL));
		worker.setState(State.valueOf(jsonObj.getString(STATE).replaceAll(SPACE, UNDERSCORE).toUpperCase()));
		worker.setTps(jsonObj.getLong(TPS));

		return worker;
	}

	public static List<Service> toServiceList(RestFabricManager restFabricManager, String json) throws JSONException {
		ArrayList<Service> apps = new ArrayList<Service>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			ServiceImpl service = new ServiceImpl(restFabricManager, jsonObj.getString(SERVICE));
			apps.add(service);
		}

		return apps;
	}

	public static List<Worker> toWorkerList(RestFabricManager restFabricManager, String json) throws JSONException {
		try {
			ArrayList<Worker> workers = new ArrayList<Worker>();

			JSONArray jsonArray = new JSONArray(json);
			for (int i = 0; i < jsonArray.length(); i++) {
				JSONObject jsonObj = jsonArray.getJSONObject(i);
				WorkerImpl worker = new WorkerImpl(restFabricManager, InetAddress.getByName(jsonObj.getString(IP)));
				workers.add(worker);
			}

			return workers;
		} catch (UnknownHostException uhe) {
			// this should never happen
			throw new RuntimeException(uhe);
		}
	}

	public static Map<InetAddress, State> toWorkerStateMap(String json) throws JSONException {
		try {
			JSONObject jsonObject = new JSONObject(json);

			Iterator<?> itr = jsonObject.keys();

			HashMap<InetAddress, State> map = new HashMap<InetAddress, State>();
			while (itr.hasNext()) {
				String ip = (String) itr.next();
				JSONObject workerJson = jsonObject.getJSONObject(ip);
				State state = toState(workerJson.toString());
				map.put(InetAddress.getByName(ip), state);
			}

			return map;
		} catch (UnknownHostException uhe) {
			// this should never happen
			throw new RuntimeException(uhe);
		}
	}
	
	public static State toState(String json) throws JSONException{
		JSONObject jsonObject = new JSONObject(json);		
		String state = jsonObject.getString(STATE);
		
		return State.valueOf(state.replaceAll(SPACE, UNDERSCORE).toUpperCase());
	}

	public static ServiceAppInfo toServiceAppInfo(String json) throws JSONException {
		JSONObject jsonObj = new JSONObject(json);

		ServiceAppInfoImpl serviceAppInfo = new ServiceAppInfoImpl();
		serviceAppInfo.setDescription(jsonObj.getString(DESCRIPTION));
		serviceAppInfo.setVersion(jsonObj.getString(VERSION));
		serviceAppInfo.setDisplayName(jsonObj.getString(DISPLAY_NAME));
		serviceAppInfo.setFile(jsonObj.getString(FILE));

		return serviceAppInfo;
	}

	public static ServiceConfigInfo toServiceConfigInfo(String json) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);
		ServiceConfigInfoImpl info = new ServiceConfigInfoImpl();

		info.setDescription(jsonObject.getString(DESCRIPTION));
		info.setDisplayName(jsonObject.getString(DISPLAY_NAME));
		info.setVersion(jsonObject.getString(VERSION));
		info.setFile(jsonObject.getString(FILE));

		return info;
	}

	public static ServiceInfo toServiceInfo(String json) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);
		ServiceInfoImpl info = new ServiceInfoImpl();

		info.setDescription(jsonObject.getString(DESCRIPTION));
		info.setDisplayName(jsonObject.getString(DISPLAY_NAME));
		info.setVersion(jsonObject.getString(VERSION));
		info.setFile(jsonObject.getString(FILE));

		return info;
	}

	public static List<ServiceApp> toServiceAppList(RestFabricManager restFabricManager, Service service, String json) throws JSONException {
		ArrayList<ServiceApp> apps = new ArrayList<ServiceApp>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			ServiceAppImpl app = new ServiceAppImpl(restFabricManager, service, jsonObj.getString(APP));
			apps.add(app);
		}

		return apps;
	}

	public static List<ServiceConfig> toServiceConfigList(RestFabricManager restFabricManager, Service service, String json) throws JSONException {
		ArrayList<ServiceConfig> list = new ArrayList<ServiceConfig>();

		JSONArray jsonArray = new JSONArray(json);
		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObj = jsonArray.getJSONObject(i);
			ServiceConfigImpl serviceConfig = new ServiceConfigImpl(restFabricManager, service, jsonObj.getString(CONFIG));
			list.add(serviceConfig);
		}

		return list;
	}

	public static WorkerInfo toWorkerInfo(String json) throws JSONException {
		try {
			JSONObject jsonObject = new JSONObject(json);
			WorkerInfoImpl info = new WorkerInfoImpl();

			info.setMacAddress(jsonObject.getString(MAC));
			// TODO possible place for ENUM
			info.setState(jsonObject.getString(STATE));
			info.setOperatingSystem(jsonObject.getString(OPERATING_SYSTEM));
			info.setCpuCount(jsonObject.getInt(CPU_COUNT));
			info.setRegionPort(jsonObject.getInt(REGION_PORT));
			info.setRegionLeader(jsonObject.getBoolean(REGION_LEADER));
			info.setRequestServiceUp(jsonObject.getBoolean(REQUEST_SERVICE_UP));
			info.setRequestSslServiceUp(jsonObject.getBoolean(RQUEST_SSL_SERVICE_UP));
			info.setInternalIp(InetAddress.getByName(jsonObject.getString(INTERNAL_IP)));
			info.setExternalIp(InetAddress.getByName(jsonObject.getString(EXTERNAL_IP)));

			return info;
		} catch (UnknownHostException uhe) {
			// this should never happen
			throw new RuntimeException(uhe);
		}
	}
	
	public static ShutdownList toShutdownList(String json) throws JSONException {
		JSONObject jsonObject = new JSONObject(json);
		ShutdownListImpl shutdownList = new ShutdownListImpl();
		
		Iterator<?> shutdownListItr = jsonObject.keys();
		while(shutdownListItr.hasNext()){
			String key = (String) shutdownListItr.next();
			
			//what map are we working with
			Map<String, ShutdownListInfo> map = null;
			if(key.equals(RESTART)){
				map = shutdownList.getRestartMap();
			}else if(key.equals(REBOOT)){
				map = shutdownList.getRebootMap();
			}else if(key.equals(RESTART_SERVICE)){
				map = shutdownList.getRestartServiceMap();
			}else if(key.equals(RESTART_STORAGE)){
				map = shutdownList.getRestartStorageMap();
			}else if(key.equals(RESTART_FAM)){
				map = shutdownList.getRestartFamMap();
			}else{
				//Unknown key
				continue;
			}
			
			// Build ShutdownListInfoMap for this key
			JSONObject shutdownListJSONObject = jsonObject.getJSONObject(key);
			Iterator<?> itr = shutdownListJSONObject.keys();
			while(itr.hasNext()){
				String ip = (String) itr.next();
				
				String ipJson = shutdownListJSONObject.getString(ip);
				ShutdownListInfo info = toShutdownListInfo(ipJson); 
				
				map.put(ip, info);
			}
			
		}
		
		return shutdownList;
	}
	
	public static ShutdownListInfo toShutdownListInfo(String json) throws JSONException{
		JSONObject jsonObject = new JSONObject(json);
		
		ShutdownListInfoImpl info = new ShutdownListInfoImpl();
		info.setMacAddress(jsonObject.getString(MAC));
		info.setClock(jsonObject.getInt(CLOCK));
		info.setState(State.valueOf(jsonObject.getString(STATE).replaceAll(SPACE, UNDERSCORE).toUpperCase()));
		info.setElapsed(jsonObject.getLong(ELAPSED));
		
		return info;
	}
		

}
